/*
 * ./src/fonction_algo.c
 * Copyright (C) 2009 lameire alexis
 * 
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see <http://www.gnu.org/licenses/>.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include <stdbool.h>
#include <gcrypt.h>
#include <sqlite3.h>
#include <glib-2.0/glib.h>
#include "fonction_algo.h"
char *tirer_un_mot(char *categorie)
{
	sqlite3 *db = NULL;
	int erreur = 0;
	printf(_("DEBUG : Début de la génération\n"));
	//verification de l'existance de la bdd
	if (access("./DB", R_OK) == -1)
	{
		fprintf(stderr, _("ERREUR : Base de données inexistante : fermeture"));
		exit(EXIT_FAILURE);
	}
	//ouverture de la bdd
	erreur = sqlite3_open_v2("DB", &db, SQLITE_OPEN_READWRITE, NULL);
	if (erreur != SQLITE_OK)
	{
		fprintf(stderr, _("ERREUR : Impossible d'ouvrir la BDD dico : fermeture\n"));
		fprintf(stderr, "%s", sqlite3_errmsg(db));
		exit(EXIT_FAILURE);
	}
	else
	{
		int i = 0;
		int taille_mot = 0;
		int id = -1;
		int nb_mot_tirable = -1;
		const unsigned char *mot = NULL;
		char *mot_a_retourner = NULL;
		char *requette_a_passer = NULL;
		sqlite3_stmt *requette = NULL;

		// verification si il est encore posible de tirer un mot
		requette_a_passer =
		    (char *)
		    g_strdup_printf
		    ("SELECT COUNT(mot) FROM mots, categorie WHERE categorie.categorie='%s' and categorie.id = mots.id_cat AND mots.deja_tire != 1;",
		     categorie);
		erreur = sqlite3_prepare_v2(db, requette_a_passer, -1, &requette, NULL);
		g_free(requette_a_passer);
		if (erreur != SQLITE_OK)
		{
			fprintf(stderr, _("ERREUR : Impossible de vérifier le nombre de mots : %d\n"), erreur);
			sqlite3_close(db);
			exit(EXIT_FAILURE);
		}
		else
		{
			sqlite3_step(requette);
			nb_mot_tirable = sqlite3_column_int(requette, 0);
			sqlite3_finalize(requette);
			if (nb_mot_tirable == 0)
			{
				char *erreur_text = NULL;
				erreur = sqlite3_exec(db, "UPDATE mots SET deja_tire = 0", NULL, NULL, &erreur_text);
				if (erreur != SQLITE_OK)
				{
					fprintf(stderr, _("ERREUR : Impossible de remettre les mots tirés à leurs valeurs initiales\n%s"),
						erreur_text);
					sqlite3_free(erreur_text);
					sqlite3_close(db);
					exit(EXIT_FAILURE);
				}
			}
		}

		//recuperation du mot
		requette_a_passer =
		    (char *)
		    g_strdup_printf
		    ("SELECT mots.id, mots.mot FROM mots, categorie WHERE categorie.categorie='%s' AND categorie.id = mots.id_cat AND mots.deja_tire != 1 ORDER BY RANDOM() LIMIT 1",
		     categorie);
		erreur = sqlite3_prepare_v2(db, requette_a_passer, -1, &requette, NULL);
		g_free(requette_a_passer);
		if (erreur != SQLITE_OK)
		{
			fprintf(stderr, _("ERREUR : Impossible de récupérer : %d\n"), erreur);
			sqlite3_close(db);
			exit(EXIT_FAILURE);
		}
		else
		{
			sqlite3_step(requette);
			id = sqlite3_column_int(requette, 0);
			mot = sqlite3_column_text(requette, 1);

			printf(_("DEBUG : Le mot est : %s\n"), mot);
			taille_mot = strlen(mot);
			mot_a_retourner = malloc(sizeof(char) * (taille_mot + 1));
			if (mot_a_retourner == NULL)
			{
				fprintf(stderr, _("ERREUR : Impossible d'allouer de la mémoire pour le mot : fermeture"));
				sqlite3_finalize(requette);
				sqlite3_close(db);
				exit(EXIT_FAILURE);
			}
			else
			{
				strcpy(mot_a_retourner, mot);
				sqlite3_finalize(requette);
			}
		}
		taille_mot = strlen(mot);

		//marquage du mot déjà tiré
		erreur = sqlite3_prepare_v2(db, "UPDATE mots SET deja_tire=1 WHERE id=?", -1, &requette, NULL);
		if (erreur != SQLITE_OK)
		{
			fprintf(stderr, _("ERREUR : Impossible de mettre à jour : %d\n"), erreur);
			sqlite3_close(db);
			exit(EXIT_FAILURE);
		}
		else
		{
			sqlite3_bind_int(requette, 1, id);
			sqlite3_step(requette);
			sqlite3_finalize(requette);
		}
		sqlite3_close(db);
		return mot_a_retourner;
	}
}

config *ouvrir_fichier_de_conf()
{
	FILE *fichier = NULL;
	fichier = fopen("config", "r");
	if (fichier == NULL)
	{
		fprintf(stderr, _("ERREUR : Impossible d'ouvrir le fichier de configuration\n"));
		exit(EXIT_FAILURE);
	}
	else
	{
		int carractere = 0;
		config *structure_a_retourner = malloc(sizeof(config));
		memset(structure_a_retourner, 0, sizeof(config));
		//preparation de la structure pour le stockage

		do
		{
			int nb_carractere_ligne = compter(fichier, '\n');
			int nb_carractere_preposition = compter(fichier, '=');
			int nb_carractere_valeur = nb_carractere_ligne - nb_carractere_preposition - 1;
			// calcul du nombre des carractere de la key et des obtions

			carractere = getc(fichier);
			if (carractere == '#')
			{
				fseek(fichier, nb_carractere_ligne - 1, SEEK_CUR);
			}
			else
			{
				fseek(fichier, -1, SEEK_CUR);
				char *key = recuperer(fichier, nb_carractere_preposition);
				fseek(fichier, 1, SEEK_CUR);
				char *value = recuperer(fichier, nb_carractere_valeur);
				// recuperation de la key et des value

				if (!strcmp(key, "server"))
				{
					if (parse_regex("[-_[:alnum:]]+\\.[-_[:alnum:]]+\\.[[:alnum:]]{2,4}", value))
					{
						structure_a_retourner->server = value;
					}
					else
					{
						fprintf(stderr, _("ERREUR : Serveur invalide \n"));
						free(value);
					}
				}
				else if (!strcmp(key, "port"))
				{
					int port = -1;
					sscanf(value, "%d", &port);
					if (port >= 0 && port <= 65535)
					{
						structure_a_retourner->port = port;
					}
					else
					{
						fprintf(stderr, _("ERREUR : Port pour le serveur invalide\n"));
					}
					free(value);
				}
				else if (!strcmp(key, "chan"))
				{
					if (parse_regex("^[#@][-_[:alnum:]]{1,31}", value))
					{
						structure_a_retourner->chan = value;
					}
					else
					{
						fprintf(stderr,
							_
							("ERREUR : Canal invalide, celui-ci doit commencer par # ou par @ et ne doit contenir au plus que 31 caractères\n"));
						free(value);
					}
				}
				else if (!strcmp(key, "pseudo"))
				{
					if (parse_regex("^[-_[:alnum:]]{1,31}", value))
					{
						structure_a_retourner->pseudo = value;
					}
					else
					{
						fprintf(stderr,
							_
							("ERREUR : Le pseudo doit comprendre entre 1 et 31 caractères et être composé de majuscules, de minuscules, de nombres, de - ou de _\n"));
						free(value);
					}
				}
				else if (!strcmp(key, "leader"))
				{
					if (parse_regex("^[-_[:alnum:]]{1,31}", value))
					{
						structure_a_retourner->pseudo_leader = value;
					}
					else
					{
						fprintf(stderr,
							_
							("ERREUR : Le pseudo du leader doit comprendre entre 1 et 31 caractères et être composé de majuscules, de minuscules, de nombres, de - ou de _ \n"));
						free(value);
					}
				}
				else if (!strcmp(key, "mdp_leader"))
				{
					if (parse_regex("^[[:digit:][:lower:]]{40}", value))
					{
						structure_a_retourner->password = value;
					}
					else
					{
						fprintf(stderr,
							_
							("ERREUR : Pour des raisons de sécurité, veuillez indiquer la somme de contrôle sha1 pour votre mot de passe\n"));
						free(value);
					}
				}
				else if (!strcmp(key, "token"))
				{
					if (parse_regex("^[!~@#]{1}", value))
					{
						structure_a_retourner->token = value[0];
					}
					else
					{
						fprintf(stderr, _("ERREUR : Les tokens autorisées sont !, ~, @ et #"));
					}
					free(value);
				}
				else if (!strcmp(key, "defaut_categorie"))
				{
					if (parse_regex("^[[:alnum:]]{1,30}", value))
					{
						structure_a_retourner->def_cat = value;
					}
					else
					{
						fprintf(stderr, _("ERREUR : Catégorie invalide"));
					}
				}
				else if (!strcmp(key, "timeout_time"))
				{
					if (parse_regex("^[0-9]{1,3}", value))
					{
						sscanf(value, "%d", &(structure_a_retourner->temps_timeout));
						free(value);
					}
					else
					{
						fprintf(stderr, _("ERREUR : Ce n'est pas un chiffre compris entre 0 et 999"));
					}
				}
				else if (key[0] == '\n')
				{
					printf(_("Fin du fichier.\n"));
					break;
				}
				else
				{
					fprintf(stderr, _("ERREUR : ligne incohérente : tentative de lecture de la ligne suivante\n"));
				}
				//stockage dans la structure
				free(key);
			}
			carractere = getc(fichier);
			//recuperation du code d'erreur pour lire le fichier ligne a ligne
		}
		while (carractere != EOF);
		fclose(fichier);
		return structure_a_retourner;
	}
}

int compter(FILE * fichier, char token)
{
	int nb_carractere_ligne = 0;
	int c = 0;

	for (nb_carractere_ligne = 0; c != token && c != EOF; nb_carractere_ligne++)
	{
		c = fgetc(fichier);
	}
	fseek(fichier, (nb_carractere_ligne * -1), SEEK_CUR);
	printf(_("DEBUG : Position courante : %ld\n"), ftell(fichier));
	return nb_carractere_ligne - 1;
}

char *recuperer(FILE * fichier, int taille)
{
	printf(_("DEBUG : Taille de la chaîne à lire : %d\n"), taille);
	if (taille <= 0)
	{
		return NULL;
	}
	else
	{
		char *erreur = 0;
		char *chaine = NULL;
		chaine = malloc(sizeof(char) * (taille + 1));
		if (chaine == NULL)
		{
			fprintf(stderr, _("ERREUR : Impossible d'allouer la mémoire : fermeture"));
			exit(EXIT_FAILURE);
		}
		else
		{
			erreur = fgets(chaine, taille + 1, fichier);
			return chaine;
		}
	}
}

int parse_regex(const char *regex, const char *phrase)
{
	regex_t regex_compiler;
	int erreur;

	erreur = regcomp(&regex_compiler, regex, REG_NOSUB | REG_EXTENDED);
	if (erreur != 0)
	{
		parse_regex_error(erreur, &regex_compiler);
	}
	else
	{
		int resultat;
		resultat = regexec(&regex_compiler, phrase, 0, NULL, 0);
		regfree(&regex_compiler);

		if (resultat == 0)
		{
			return 1;
		}
		else if (resultat == REG_NOMATCH)
		{
			return 0;
		}
		else
		{
			parse_regex_error(erreur, &regex_compiler);
		}
	}
}

void parse_regex_error(int erreur, regex_t * regex_compiler)
{
	char *texte_erreur = NULL;
	size_t taille_erreur;

	taille_erreur = regerror(erreur, regex_compiler, NULL, 0);
	texte_erreur = malloc(sizeof(char) * taille_erreur);
	if (texte_erreur == NULL)
	{
		fprintf(stderr,
			_
			("Une erreur s'est produite lors de l'interprétation du fichier de configuration, mais l'allocation de la chaîne de carractére allouant l'erreur exacte a échoué.\n"));
	}
	else
	{
		regerror(erreur, regex_compiler, texte_erreur, taille_erreur);
		fprintf(stderr, _("%s : Fermeture pour erreur fatale"), texte_erreur);
		free(texte_erreur);
	}

	exit(EXIT_FAILURE);
}

void afficher_en_couleur(FILE * flux, unsigned int couleur_texte, unsigned int couleur_arriere_plan, char *texte)
{
	if (couleur_texte != 0)
	{
		fprintf(flux, "\033[%dm", couleur_texte);
	}
	if (couleur_arriere_plan != 0)
	{
		fprintf(flux, "\033[%dm", couleur_arriere_plan);
	}
	fprintf(flux, "%s", texte);
	fprintf(flux, "\033[0m");
}

void couper_espace(char *ligne)
{
	int longeur_chaine = strlen(ligne);
	int i = 0;
	for (i = longeur_chaine - 1; isspace(ligne[i]); i--)
	{
		ligne[i] = '\0';
	}
}

void copier_fichier(char *src, char *dest)
{
	FILE *fichier_src = NULL;
	FILE *fichier_dest = NULL;
	fichier_src = fopen(src, "r");
	fichier_dest = fopen(dest, "w");
	if (fichier_src == NULL || fichier_dest == NULL)
	{
		fprintf(stderr, _("ERREUR : Impossible d'ouvrir les fichiers pour la copie"));
		exit(EXIT_FAILURE);
	}
	else
	{
		char carractere = '\0';
		while ((carractere = fgetc(fichier_src)) != EOF)
		{
			fputc(carractere, fichier_dest);
		}
		fclose(fichier_src);
		fclose(fichier_dest);
	}
}

void suprimer_ligne(char *src, int curseur_debut)
{
	int taille_chaine = strlen(src);
	char *dest = NULL;

	dest = malloc(sizeof(char) * (taille_chaine + 5));
	if (dest == NULL)
	{
		fprintf(stderr, _("ERREUR : Impossible d'allouer la mémoire pour le nom du fichier temporaire pour supprimer une ligne"));
		exit(EXIT_FAILURE);
	}
	else
	{
		FILE *fichier_src = NULL;
		FILE *fichier_dest = NULL;
		strcpy(dest, src);
		strcat(dest, ".tmp");
		fichier_src = fopen(src, "r");
		fichier_dest = fopen(dest, "w");
		if (fichier_src == NULL || fichier_dest == NULL)
		{
			fprintf(stderr, _("ERREUR : Impossible d'ouvrir les fichiers pour la copie"));
			exit(EXIT_FAILURE);
		}
		else
		{
			char carractere = '\0';
			int i = 0;
			for (i = 0; i != curseur_debut; i++)
			{
				carractere = fgetc(fichier_src);
				fputc(carractere, fichier_dest);
			}
			while (fgetc(fichier_src) != '\n');
			while ((carractere = fgetc(fichier_src)) != EOF)
			{
				fputc(carractere, fichier_dest);
			}
			fclose(fichier_src);
			fclose(fichier_dest);

			remove(src);
			rename(dest, src);

			free(dest);
		}
	}
}

int verifier_mot(FILE * fichier, char *mot)
{
	int taille_mot = strlen(mot);
	int i = 0;
	bool mauvaise_ligne = false;
	char carractere = 0;
	fseek(fichier, 0, SEEK_SET);
	while ((carractere = fgetc(fichier)) != EOF)
	{
		if (carractere == '\n')
		{
			i = -1;
			mauvaise_ligne = false;
		}
		else if (mauvaise_ligne == false)
		{
			if (mot[i] == carractere)
			{
				if (i == (taille_mot - 1))
				{
					return ftell(fichier) - taille_mot;
				}
			}
			else
			{
				mauvaise_ligne = true;
			}
		}
		i++;
	}
	return -1;
}

char *sha1sun(char *key)
{
	int taille_message = strlen(key);
	int taille_resultat = gcry_md_get_algo_dlen(GCRY_MD_SHA1);
	unsigned char *hash_binaire = NULL;
	hash_binaire = malloc(sizeof(char) * taille_resultat);
	if (hash_binaire == NULL)
	{
		fprintf(stderr, _("ERREUR : impossible d'allouer la chaîne de hash binaire : fermeture\n"));
		exit(EXIT_FAILURE);
	}
	else
	{
		char *hash_hexa = NULL;
		hash_hexa = malloc(sizeof(char) * ((taille_resultat * 2) + 1));
		if (hash_hexa == NULL)
		{
			fprintf(stderr, _("ERREUR : impossible d'allouer la chaîne de hash hexadécimale : fermeture"));
			exit(EXIT_FAILURE);
		}
		else
		{
			int i = 0;
			gcry_md_hash_buffer(GCRY_MD_SHA1, hash_binaire, key, taille_message);
			for (i = 0; i < taille_resultat; i++)
			{
				snprintf(hash_hexa + i * 2, 3, "%02x", hash_binaire[i]);
			}
			free(hash_binaire);
			return hash_hexa;
		}
	}
}

int recuperer_args_ajouter(const char *src, char **categorie, char **mot)
{
	int taille_src = strlen(src);
	int taille_categorie = 0;
	int taille_mot = 0;
	char *categorie_a_retourner = NULL;
	char *mot_a_retourner = NULL;

	for (taille_categorie = 0; src[taille_categorie] != ' '; taille_categorie++)
	{
		if (src[taille_categorie] == '\0')
		{
			return 1;
		}
	}
	taille_mot = taille_src - taille_categorie - 1;

	mot_a_retourner = malloc(sizeof(char) * (taille_mot + 1));
	categorie_a_retourner = malloc(sizeof(char) * (taille_categorie + 1));
	if (mot_a_retourner == NULL || categorie_a_retourner == NULL)
	{
		fprintf(stderr, _("ERREUR : Manque de mémoire pour le mot et la catégorie à ajouter à la BDD : fermeture"));
		exit(EXIT_FAILURE);
	}
	else
	{
		strncpy(categorie_a_retourner, src, taille_categorie);
		categorie_a_retourner[taille_categorie] = '\0';
		strncpy(mot_a_retourner, &src[taille_categorie + 1], taille_mot);
		mot_a_retourner[taille_mot] = '\0';
		*mot = mot_a_retourner;
		*categorie = categorie_a_retourner;
		return 0;
	}
}

int compter_colonnes(void *nb_appel, int argc, char **argv, char **nom_colones)
{
	int *appel_courrent = (int *) nb_appel;
	printf(_("DEBUG : Incrémentation avant compteur : %p   %d\n"), appel_courrent, *appel_courrent);
	(*appel_courrent)++;
	printf(_("DEBUG : Incrémentation du compteur : %p  %d\n"), appel_courrent, *appel_courrent);

	return 0;
}

void ajouter_score(char *pseudo, int score)
{
	sqlite3 *db = NULL;
	if (sqlite3_open_v2("DB", &db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK)
	{
		fprintf(stderr, _("ERREUR : Impossible d'ouvrir la BDD\n%d\n"), sqlite3_errmsg(db));
		exit(EXIT_FAILURE);
	}
	else
	{
		int erreur = 0;
		char *msg_erreur = NULL;
		char *requette = (char *) g_strdup_printf("INSERT INTO utilisateur(pseudo) VALUES('%s')", pseudo);

		erreur = sqlite3_exec(db, requette, NULL, NULL, &msg_erreur);
		g_free(requette);

		if (erreur != SQLITE_OK && erreur != SQLITE_CONSTRAINT)
		{
			fprintf(stderr, _("ERREUR : Impossible d'ajouter l'utilisateur\n%s\n"), msg_erreur);
			sqlite3_free(msg_erreur);
			sqlite3_close(db);
			exit(EXIT_FAILURE);
		}
		else
		{
			if (erreur == SQLITE_CONSTRAINT)
			{
				sqlite3_free(msg_erreur);
			}
			requette = g_strdup_printf("UPDATE utilisateur " "SET score=score+%d " "WHERE pseudo='%s'", score, pseudo);
			printf("%s\n", requette);
			erreur = sqlite3_exec(db, requette, NULL, NULL, &msg_erreur);
			sqlite3_close(db);
			if (erreur != SQLITE_OK)
			{
				fprintf(stderr, _("ERREUR : Impossible de mettre à jour les scores\n%s\n"), msg_erreur);
				sqlite3_free(msg_erreur);
				exit(EXIT_FAILURE);
			}
		}
	}
}


void hl_users(irc_session_t * session, char *chan)
{
	char buffer[394] = { '\0' };
	sqlite3 *db = NULL;

	if (sqlite3_open_v2("DB", &db, SQLITE_OPEN_READWRITE, NULL) != SQLITE_OK)
	{
		fprintf(stderr, _("ERREUR : Impossible d'ouvrir la BDD ; fermeture\n%s\n"), sqlite3_errmsg(db));
		exit(EXIT_FAILURE);
	}
	else
	{
		sqlite3_stmt *requette = NULL;
		int taille_buffer_restante = 393;
		int erreur = sqlite3_prepare_v2(db, "SELECT pseudo FROM utilisateur WHERE hl_me=1", -1, &requette, NULL);
		if (erreur != SQLITE_OK)
		{
			fprintf(stderr, _("ERREUR : Impossible de récupérer les pseudos : fermeture\n"));
			exit(EXIT_FAILURE);
		}
		else
		{
			while (sqlite3_step(requette) == SQLITE_ROW)
			{
				const unsigned char *pseudo = sqlite3_column_text(requette, 0);
				int taille_pseudo = strlen(pseudo);
				if (taille_buffer_restante < (taille_pseudo + 2))
				{
					irc_cmd_msg(session, chan, buffer);
					taille_buffer_restante = 393;
					memset(buffer, 0, sizeof(char) * taille_buffer_restante);
				}
				sprintf(&buffer[393 - taille_buffer_restante], "%s, ", pseudo);
				taille_buffer_restante -= (taille_pseudo + 2);
			}
			irc_cmd_msg(session, chan, buffer);
			sqlite3_finalize(requette);
			sqlite3_close(db);
		}
	}
}
